﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

using SmlEngine.Sprites;
using SmlEngine.Sprites.Base;
using SmlEngine.Sprites.Settings;
using SmlEngine.Graphics;
using SmlEngine.Sprites.Base.WorldMap;
using SmlEngine.Extensions;
using SmlEngine.IO;

namespace SmlEngine.Sprites.Collections
{
    public static class WorldObjectSet
    {
        #region Internal Tiles
        public static WorldMapTile LoadWorldMapTile(int id, GraphicsDevice gd, ContentManager cm)
        {
            if (id >= 0 && id <= 100)
            {
                /* SMB3 Grass Center */
                if (id == 4) return new WorldMapTile(@"world\ow_terrain_grass", rect(0, 128, 16, 16), cm);
            }
            return null;
        }

        public static WorldAnimatedTile LoadWorldAnimatedTile(int id, GraphicsDevice gd, ContentManager cm)
        {
            if (id >= 0 && id <= 100)
            {
                /* SMB3 Grass Top Left */
                if (id == 0)
                {
                    List<Rectangle> r = new List<Rectangle>() { rect(192, 16, 16, 16), rect(208, 16, 16, 16), rect(224, 16, 16, 16), rect(240, 16, 16, 16) };
                    return new WorldAnimatedTile(0, @"world\ow_terrain_grass", r, cm, 20);
                }
                /* SMB3 Grass Top */
                else if (id == 1)
                {
                    List<Rectangle> r = new List<Rectangle>() { rect(192, 0, 16, 16), rect(208, 0, 16, 16), rect(224, 0, 16, 16), rect(240, 0, 16, 16) };
                    return new WorldAnimatedTile(1, @"world\ow_terrain_grass", r, cm, 20);
                }
                /* SMB3 Grass Top Right */
                else if (id == 2)
                {
                    List<Rectangle> r = new List<Rectangle>() { rect(128, 16, 16, 16), rect(144, 16, 16, 16), rect(160, 16, 16, 16), rect(176, 16, 16, 16) };
                    return new WorldAnimatedTile(2, @"world\ow_terrain_grass", r, cm, 20);
                }
                /* SMB3 Grass Left */
                else if (id == 3)
                {
                    List<Rectangle> r = new List<Rectangle>() { rect(0, 0, 16, 16), rect(16, 0, 16, 16), rect(32, 0, 16, 16), rect(48, 0, 16, 16) };
                    return new WorldAnimatedTile(3, @"world\ow_terrain_grass", r, cm, 20);
                }
                /* SMB3 Grass Right */
                else if (id == 5)
                {
                    List<Rectangle> r = new List<Rectangle>() { rect(64, 0, 16, 16), rect(80, 0, 16, 16), rect(96, 0, 16, 16), rect(112, 0, 16, 16) };
                    return new WorldAnimatedTile(5, @"world\ow_terrain_grass", r, cm, 20);
                }
                /* SMB3 Grass Bottom Left */
                else if (id == 6)
                {
                    List<Rectangle> r = new List<Rectangle>() { rect(0, 16, 16, 16), rect(16, 16, 16, 16), rect(32, 16, 16, 16), rect(48, 16, 16, 16) };
                    return new WorldAnimatedTile(6, @"world\ow_terrain_grass", r, cm, 20);
                }
                /* SMB3 Grass Bottom */
                else if (id == 7)
                {
                    List<Rectangle> r = new List<Rectangle>() { rect(128, 0, 16, 16), rect(144, 0, 16, 16), rect(160, 0, 16, 16), rect(176, 0, 16, 16) };
                    return new WorldAnimatedTile(7, @"world\ow_terrain_grass", r, cm, 20);
                }
                /* SMB3 Grass Bottom Right */
                else if (id == 8)
                {
                    List<Rectangle> r = new List<Rectangle>() { rect(64, 16, 16, 16), rect(80, 16, 16, 16), rect(96, 16, 16, 16), rect(112, 16, 16, 16) };
                    return new WorldAnimatedTile(8, @"world\ow_terrain_grass", r, cm, 20);
                }
                /* SMB3 Grass Corner Bottom Right */
                else if (id == 9)
                {
                    List<Rectangle> r = new List<Rectangle>() { rect(64, 96, 16, 16), rect(80, 96, 16, 16), rect(96, 96, 16, 16), rect(112, 96, 16, 16) };
                    return new WorldAnimatedTile(9, @"world\ow_terrain_grass", r, cm, 20);
                }
                /* SMB3 Grass Corner Bottom Left */
                else if (id == 10)
                {
                    List<Rectangle> r = new List<Rectangle>() { rect(0, 96, 16, 16), rect(16, 96, 16, 16), rect(32, 96, 16, 16), rect(48, 96, 16, 16) };
                    return new WorldAnimatedTile(10, @"world\ow_terrain_grass", r, cm, 20);
                }
                /* SMB3 Grass Corner Top Right */
                else if (id == 11)
                {
                    List<Rectangle> r = new List<Rectangle>() { rect(128, 80, 16, 16), rect(144, 80, 16, 16), rect(160, 80, 16, 16), rect(176, 80, 16, 16) };
                    return new WorldAnimatedTile(11, @"world\ow_terrain_grass", r, cm, 20);
                }
                /* SMB3 Grass Corner Top Left */
                else if (id == 12)
                {
                    List<Rectangle> r = new List<Rectangle>() { rect(192, 80, 16, 16), rect(208, 80, 16, 16), rect(224, 80, 16, 16), rect(240, 80, 16, 16) };
                    return new WorldAnimatedTile(12, @"world\ow_terrain_grass", r, cm, 20);
                }
            }
            return null;
        }

        public static PathTile LoadPathTile(int id, GraphicsDevice gd, ContentManager cm)
        {
            if (id >= 0 && id <= 100)
            {
                /* SMB3 Horizontal Node */
                if (id == 0)
                {
                    List<Vector2> v = new List<Vector2>() { new Vector2(0, 0), new Vector2(16, 0), new Vector2 (32, 0), new Vector2(48, 0) };
                    return new PathTile(cm, gd, @"world\ow_path", v, new Vector2(16, 16), PathType.Horizontal, 20, true);
                }
                /* SMB3 Vertical Node */
                else if (id == 1)
                {
                    List<Vector2> v = new List<Vector2>() { new Vector2(0, 16), new Vector2(16, 16), new Vector2(32, 16), new Vector2(48, 16) };
                    return new PathTile(cm, gd, @"world\ow_path", v, new Vector2(16, 16), PathType.Vertical, 20, true);
                }
                /* SMB3 Corner Bottom Right Path */
                else if (id == 2)
                {
                    List<Vector2> v = new List<Vector2>() { new Vector2(0, 32), new Vector2(16, 32), new Vector2(32, 32), new Vector2(48, 32) };
                    return new PathTile(cm, gd, @"world\ow_path", v, new Vector2(16, 16), PathType.CornerBottomRight, 20, true);
                }
                /* SMB3 Corner Top Left Path */
                else if (id == 3)
                {
                    List<Vector2> v = new List<Vector2>() { new Vector2(0, 48), new Vector2(16, 48), new Vector2(32, 48), new Vector2(48, 48) };
                    return new PathTile(cm, gd, @"world\ow_path", v, new Vector2(16, 16), PathType.CornerTopLeft, 20, false);
                }
                /* SMB3 Horizontal Path */
                else if (id == 4)
                {
                    return new PathTile(cm, gd, @"world\ow_path", new Vector2(0, 64), new Vector2(16, 16), PathType.Horizontal, false);
                }
                /* SMB3 Vertical Path */
                else if (id == 5)
                {
                    return new PathTile(cm, gd, @"world\ow_path", new Vector2(16, 64), new Vector2(16, 16), PathType.Vertical, false);
                }
            }
            return null;
        }

        public static WorldPlayer LoadWorldPlayer(int id, GraphicsDevice gd, ContentManager cm)
        {
            /* Mario */
            if (id == 0)
            {
                List<List<Vector2>> v = new List<List<Vector2>>(){ new List<Vector2>(){ new Vector2(0, 0), new Vector2(16, 0) } };
                return new WorldPlayer(cm, @"world\ow_player", WorldPlayerPoses.OnePose, v, new Vector2(16, 16), 20, gd, Vector2.Zero, 0, 0, 0);
            }
            /* Luigi */
            else if (id == 1)
            {
                List<List<Vector2>> v = new List<List<Vector2>>() { new List<Vector2>() { new Vector2(0, 16), new Vector2(16, 16) } };
                return new WorldPlayer(cm, @"world\ow_player", WorldPlayerPoses.OnePose, v, new Vector2(16, 16), 20, gd, Vector2.Zero, 0, 0, 0);
            }
            return null;
        }

        public static LevelTile LoadLevelTile(int id, GraphicsDevice gd, ContentManager cm)
        {
            /* test tile */
            if (id == 0)
            {
                Vector2 size = new Vector2(16, 16);
                List<Rectangle> r = new List<Rectangle>() { Vector2.Zero.ToRectangle(size), new Vector2(16, 0).ToRectangle(size), new Vector2(32, 0).ToRectangle(size), new Vector2(48, 0).ToRectangle(size) };
                return new LevelTile(gd, cm, @"world\ow_level", r, 10);
            }
            /* test SMB3 tile */
            else if (id == 1)
            {
                List<Rectangle> locked  = new List<Rectangle>() { rect(64, 0, 16, 16) };
                List<Rectangle> unlocking = new List<Rectangle>() { rect(80, 0, 16, 16), rect(96, 0, 16, 16), rect(112, 0, 16, 16) };
                List<Rectangle> unlocked = new List<Rectangle>() { rect(0, 16, 16, 16) };
                int unlockingFrameTime = 10;

                return new SMB3LevelTile(gd, cm, @"world\ow_level", locked, unlocking, unlocked, UnlockedGraphicsType.Static, unlockingFrameTime);
            }
            return null;
        }

        public static PipeTile LoadPipeTile(int id, GraphicsDevice gd, ContentManager cm)
        {
            if (id >= 0 && id <= 100)
            {
                if (id == 0)
                {
                    /* SMB3 Pipe Tile */
                    return new PipeTile(cm, gd, @"world\ow_level", rect(48, 32, 16, 16));
                }
            }
            return null;
        }
        #endregion

        #region Custom Tiles

        public static WorldMapTile LoadCustomStaticTile(string worldFolder, string configPath, GraphicsDevice gd)
        {
            DataReader reader = new DataReader(configPath);
            if (reader.SectionExists("[tile]"))
            {
                var values = reader.ReadFullSection("[tile]");
                if (values["type"] == "static")
                {
                    string name = values["name"];
                    string source = worldFolder + @"\" + values["source"];
                    Vector2 origin = GetVector(values["rect"]);
                    Vector2 size = GetVector(values["size"]);
                    Rectangle rect = new Rectangle((int)origin.X, (int)origin.Y, (int)size.X, (int)size.Y);

                    WorldMapTile t = new WorldMapTile(gd, source, rect);
                    return t;
                }
                else { throw new ArgumentException(string.Format("This configuration at {0} is not a static tile config.  Please check your file.", configPath)); }
            }
            else { throw new ArgumentException(string.Format("This config file at {0} is corrupt or incorrect.  Please check it.", configPath)); }
        }

        public static WorldAnimatedTile LoadCustomAnimatedTile(string worldFolder, string configPath, GraphicsDevice gd)
        {
            DataReader reader = new DataReader(configPath);
            if (reader.SectionExists("[tile]"))
            {
                var values = reader.ReadFullSection("[tile]");
                if (values["type"] == "animated")
                {
                    string name = values["name"];
                    string source = worldFolder + @"\" + values["source"];
                    int frames = Int32.Parse(values["frames"]);
                    var frameOrigins = GetMultiVector2(values["rect"]);
                    Vector2 size = GetVector(values["size"]);
                    int frameTime = Int32.Parse(values["frametime"]);

                    WorldAnimatedTile t = new WorldAnimatedTile(gd, source, frameOrigins.ToRectangleList(size), null, frameTime);
                    return t;
                }
                else { throw new Exception(string.Format("This configuration at {0} is not for an animated tile.  Please check your file.", configPath)); }
            }
            else { throw new ArgumentException(string.Format("This config file at {0} is corrupt or incorrect.  Please check it.", configPath)); }
        }

        public static WorldPlayer LoadCustomWorldPlayer(string worldFolder, string configPath, GraphicsDevice gd)
        {
            DataReader reader = new DataReader(configPath);
            if (reader.SectionExists("[worldplayer]"))
            {
                var values = reader.ReadFullSection("[worldplayer]");
                string source = worldFolder + @"\" + values["source"];
                int poses = Int32.Parse(values["poses"]);
                int frameCount = Int32.Parse(values["frames"]);
                List<List<Vector2>> frames = new List<List<Vector2>>();
                WorldPlayerPoses poseType = WorldPlayerPoses.OnePose;
                if (poses == 1)
                {
                    poseType = WorldPlayerPoses.OnePose;
                    frames.Add(GetMultiVector2(values["Frames"]));
                }
                else if (poses == 4)
                {
                    poseType = WorldPlayerPoses.FourPoses;
                    frames.Add(GetMultiVector2(values["upFrames"]));
                    frames.Add(GetMultiVector2(values["downFrames"]));
                    frames.Add(GetMultiVector2(values["leftFrames"]));
                    frames.Add(GetMultiVector2(values["rightFrames"]));
                }
                else
                {
                    throw new Exception(string.Format("Invalid number {0} of poses.  Please choose either 1 or 4.", poses));
                }
                Vector2 size = GetVector(values["size"]);
                int frameTime = Int32.Parse(values["frameTime"]);


                return new WorldPlayer(null, source, poseType, frames, size, frameTime, gd, Vector2.Zero, 0, 0, 0);
            }
            else throw new ArgumentException(string.Format("This config file at {0} is corrupt or incorrect.  Please check it.", configPath));
        }

        public static PathTile LoadCustomPathTile(string worldFolder, string configPath, GraphicsDevice gd)
        {
            DataReader reader = new DataReader(configPath);
            if (reader.SectionExists("[path]"))
            {
                var values = reader.ReadFullSection("[path]");

                string name = values["name"];
                string source = worldFolder + @"\" + values["source"];
                string type = values["type"];
                List<Vector2> sourceRects = new List<Vector2>();
                int frameTime;
                if (type == "static") sourceRects.Add(GetVector(values["rect"]));
                else if (type == "animated") sourceRects = GetMultiVector2(values["rect"]); frameTime = Int32.Parse(values["frameTime"]);
                Vector2 size = GetVector(values["size"]);
                string direction = values["direction"];
                bool node = values["node"] == "true" ? true : false;

                PathType dir = PathType.Default;
                string[] dir_names = { "vertical", "horizontal", "corner_tl", "corner_tr", "corner_bl", "corner_br", "tint_up", "tint_down", "tint_left", "tint_right", "fourway_inter" };
                PathType[] dir_values = { PathType.Vertical, PathType.Horizontal, PathType.CornerTopLeft, PathType.CornerTopRight, PathType.CornerBottomLeft, PathType.CornerBottomRight, PathType.TIntersectionFromTop, PathType.TIntersectionFromBottom, PathType.TIntersectionFromLeft, PathType.TIntersectionFromRight, PathType.FourWayIntersection };
                dir = dir_values[Array.IndexOf(dir_names, direction)];

                if (type == "static")
                {
                    return new PathTile(null, gd, source, sourceRects[0], size, dir, node);
                }
                else if (type == "animated")
                {
                    return new PathTile(null, gd, source, sourceRects, size, dir, frameTime, node);
                }
            }
            else
                throw new Exception(string.Format("The configuration file at {0} is corrupt or invalid.", configPath));
            return null;
        }

        public static LevelTile LoadCustomLevelTile(string worldFolder, string configPath, GraphicsDevice gd)
        {
            DataReader reader = new DataReader(configPath);
            if (reader.SectionExists("[level_tile]"))
            {
                var values = reader.ReadFullSection("[level_tile]");

                string name = values["name"];
                string source = worldFolder + @"\" + values["source"];
                int frames = int.Parse(values["frames"]);
                var sourceOrigins = GetMultiVector2(values["origin"]);
                int frameTime = 0;
                if (frames > 1) frameTime = Int32.Parse(values["frameLength"]);
                Vector2 size = GetVector(values["size"]);

                return new LevelTile(gd, null, source, sourceOrigins.ToRectangleList(size), frameTime);
            }
            else
            {
                throw new Exception(string.Format("The level tile configuration at {0} is corrupt or invalid.", configPath));
            }
        }

        public static SMB3LevelTile LoadCustomSMB3LevelTile(string worldFolder, string configPath, GraphicsDevice gd)
        {
            DataReader reader = new DataReader(configPath);
            if (reader.SectionExists("[smb3leveltile]"))
            {
                var values = reader.ReadFullSection("[smb3leveltile]");

                string name = values["name"];
                string source = worldFolder + @"\" + values["source"];
                Vector2 size = GetVector(values["size"]);
                var lockedRects = GetMultiVector2(values["locked"]);
                var unlockingRects = GetMultiVector2(values["unlocking"]);
                var unlockedRects = GetMultiVector2(values["unlocked"]);

                UnlockedGraphicsType type = UnlockedGraphicsType.Animated;
                string unlockedType = values["unlocked_type"];
                if (unlockedType == "static") type = UnlockedGraphicsType.Static;
                else if (unlockedType == "animated") type = UnlockedGraphicsType.Animated;

                int frameTime = int.Parse(values["frame_time"]);

                SMB3LevelTile l = new SMB3LevelTile(gd, null, source, lockedRects.ToRectangleList(size), unlockedRects.ToRectangleList(size), unlockedRects.ToRectangleList(size), type, frameTime);
                return l;
            }
            else
            {
                throw new Exception(string.Format("The lockable level tile configuration file at {0} is corrupt or invalid.", configPath));
            }
        }

        public static PipeTile LoadCustomPipeTile(string worldFolder, string configPath, GraphicsDevice gd)
        {
            DataReader reader = new DataReader(configPath);
            if (reader.SectionExists("[pipe]"))
            {
                var section = reader.ReadFullSection("[pipe]");
                string editorDisplayName = section["name"];
                string graphicsSource = section["source"];
                int frames = Int32.Parse(section["frames"]);
                var frameOrigins = GetMultiVector2(section["origin"]);
                Vector2 frameSize = GetVector(section["size"]);

                if (frames == 1)
                {
                    Rectangle area = frameOrigins[0].ToRectangle(frameSize);
                    return new PipeTile(null, gd, graphicsSource, area);
                }
                else if (frames > 1)
                {
                    List<Rectangle> areas = new List<Rectangle>();
                    frameOrigins.ForEach(r => areas.Add(r.ToRectangle(frameSize)));
                    return new PipeTile(null, gd, graphicsSource, areas);
                }
            }
            return null;
        }
        #endregion

        #region Helper Methods
        private static Rectangle rect(int x, int y, int width, int height)
        {
            return new Rectangle(x, y, width, height);
        }

        private static Vector2 GetVector(string input)
        {
            String[] v = input.Split(',');
            v[0] = v[0].Trim();
            return new Vector2(float.Parse(v[0]), float.Parse(v[1]));
        }

        private static List<Vector2> GetMultiVector2(string input)
        {
            string[] value = input.Split('-');
            int entryCount = input.Length - input.Replace("(", "").Length;
            List<Vector2> result = new List<Vector2>();

            for (int i = 0; i < entryCount; i++)
            {
                value[i] = value[i].Trim('(', ')');
                result.Add(GetVector(value[i]));
            }
            return result;
        }
        #endregion
    }
}
